use pest::Parser;
use pest_derive::Parser;

// 引入 pest 文法文件
#[derive(Parser)]
#[grammar = "lexer.pest"]
struct SysYLexer;

// 定义令牌类型
#[derive(Debug, Clone, PartialEq)]
pub enum Token {
    // 关键字
    Const,    // 'const'
    Int,      // 'int'
    Void,     // 'void'
    If,       // 'if'
    Else,     // 'else'
    While,    // 'while'
    Break,    // 'break'
    Continue, // 'continue'
    Return,   // 'return'
    // 运算符
    Plus,   // '+'
    Minus,  // '-'
    Mul,    // '*'
    Div,    // '/'
    Mod,    // '%'
    Assign, // '='
    Eq,     // '=='
    Neq,    // '!='
    Lt,     // '<'
    Gt,     // '>'
    Le,     // '<='
    Ge,     // '>='
    Not,    // '!'
    And,    // '&&'
    Or,     // '||'
    // 标点符号
    LParen,    // '('
    RParen,    // ')'
    LBrace,    // '{'
    RBrace,    // '}'
    LBracket,  // '['
    RBracket,  // ']'
    Comma,     // ','
    Semicolon, // ';'
    // 标识符和整数常量
    Ident(String),     // 标识符，如变量名
    IntegerConst(i64), // 整数常量（存储十进制值）
}

/// 将整数常量字符串转换为十进制值
fn parse_integer_const(s: &str) -> Result<i64, String> {
    // 去除首尾空白字符
    let s = s.trim();
    
    if s.starts_with("0x") || s.starts_with("0X") {
        // 十六进制
        i64::from_str_radix(&s[2..], 16).map_err(|_| format!("Invalid hex number: {}", s))
    } else if s.starts_with('0') && s.len() > 1 && s.chars().all(|c| c >= '0' && c <= '7') {
        // 八进制：仅当且仅当恰好一个前导 0 时按八进制解释；多个前导 0 视为十进制前导零
        let leading_zeros = s.chars().take_while(|&c| c == '0').count();
        if leading_zeros == 1 {
            i64::from_str_radix(&s[1..], 8).map_err(|_| format!("Invalid octal number: {}", s))
        } else {
            // 视为十进制并去除前导零
            let t = s.trim_start_matches('0');
            if t.is_empty() {
                Ok(0)
            } else {
                t.parse::<i64>().map_err(|_| format!("Invalid decimal number: {}", s))
            }
        }
    } else {
        // 十进制（包括前导零）
        let t = s.trim_start_matches('0');
        if t.is_empty() {
            Ok(0)
        } else {
            t.parse::<i64>()
                .map_err(|_| format!("Invalid decimal number: {}", s))
        }
    }
}

/// 词法分析函数，将输入字符串解析为令牌序列
///
/// # 参数
/// - `input`: 输入的源代码字符串
///
/// # 返回值
/// - 成功时返回 `Vec<(Token, usize)>`，包含令牌及其行号
/// - 失败时返回错误信息，包含非法字符及其行号
pub fn tokenize(input: &str) -> Result<Vec<(Token, usize)>, (String, usize)> {
    let mut tokens = Vec::new();

    // 启用详细错误信息
    pest::set_error_detail(true);
    // 使用 SysYLexer 解析输入字符串
    let pairs = match SysYLexer::parse(Rule::file, input) {
        Ok(pairs) => pairs,
        Err(e) => {
            let message = format!("Unexpected character: {}", e);
            let line_col_location = e.line_col;
            let line_no = match line_col_location {
                pest::error::LineColLocation::Pos((line, _)) => line,
                pest::error::LineColLocation::Span((line, _), _) => line,
            };
            return Err((message, line_no));
        }
    };

    // 遍历所有子节点（不包含顶层的 file）
    for pair in pairs.flatten() {
        let span = pair.as_span();
        let line_no = span.start_pos().line_col().0;

        match pair.as_rule() {
            // 关键字
            Rule::CONST => tokens.push((Token::Const, line_no)),
            Rule::INT => tokens.push((Token::Int, line_no)),
            Rule::VOID => tokens.push((Token::Void, line_no)),
            Rule::IF => tokens.push((Token::If, line_no)),
            Rule::ELSE => tokens.push((Token::Else, line_no)),
            Rule::WHILE => tokens.push((Token::While, line_no)),
            Rule::BREAK => tokens.push((Token::Break, line_no)),
            Rule::CONTINUE => tokens.push((Token::Continue, line_no)),
            Rule::RETURN => tokens.push((Token::Return, line_no)),
            // 运算符
            Rule::PLUS => tokens.push((Token::Plus, line_no)),
            Rule::MINUS => tokens.push((Token::Minus, line_no)),
            Rule::MUL => tokens.push((Token::Mul, line_no)),
            Rule::DIV => tokens.push((Token::Div, line_no)),
            Rule::MOD => tokens.push((Token::Mod, line_no)),
            Rule::ASSIGN => tokens.push((Token::Assign, line_no)),
            Rule::EQ => tokens.push((Token::Eq, line_no)),
            Rule::NEQ => tokens.push((Token::Neq, line_no)),
            Rule::LT => tokens.push((Token::Lt, line_no)),
            Rule::GT => tokens.push((Token::Gt, line_no)),
            Rule::LE => tokens.push((Token::Le, line_no)),
            Rule::GE => tokens.push((Token::Ge, line_no)),
            Rule::NOT => tokens.push((Token::Not, line_no)),
            Rule::AND => tokens.push((Token::And, line_no)),
            Rule::OR => tokens.push((Token::Or, line_no)),
            // 标点符号
            Rule::L_PAREN => tokens.push((Token::LParen, line_no)),
            Rule::R_PAREN => tokens.push((Token::RParen, line_no)),
            Rule::L_BRACE => tokens.push((Token::LBrace, line_no)),
            Rule::R_BRACE => tokens.push((Token::RBrace, line_no)),
            Rule::L_BRACKT => tokens.push((Token::LBracket, line_no)),
            Rule::R_BRACKT => tokens.push((Token::RBracket, line_no)),
            Rule::COMMA => tokens.push((Token::Comma, line_no)),
            Rule::SEMICOLON => tokens.push((Token::Semicolon, line_no)),
            // 标识符
            Rule::IDENT => tokens.push((Token::Ident(pair.as_str().trim().to_string()), line_no)),
            Rule::IDENT_START => continue,
            // 整数常量
            Rule::INTEGER_CONST => {
                let value = parse_integer_const(pair.as_str()).map_err(|msg| (msg, line_no))?;
                tokens.push((Token::IntegerConst(value), line_no));
            }
            // 跳过空白
            Rule::WHITESPACE => continue,
            // 处理非法字符
            Rule::INVALID_CHAR => {
                let message = format!("Mysterious character \"{}\".", pair.as_str());
                return Err((message, line_no));
            }
            Rule::file => continue,
            Rule::EOI => continue,
            Rule::COMMENT => continue,
            Rule::LINE_COMMENT => continue,
            Rule::MULTILINE_COMMENT => continue,
            _ => continue,
        }
    }
    Ok(tokens)
}
